package com.mars.sso.client.filter;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;
import com.mars.base.common.utils.CookieUtils;
import com.mars.base.common.utils.date.DateUtil;
import com.mars.base.common.utils.http.HttpHelper;
import com.mars.base.common.utils.http.HttpResult;

public class TokenManager {
	
	static Logger logger = LoggerFactory.getLogger(TokenManager.class);

	// 业务系统与SSO系统间通信的内网地址
	static String serverInnerAddress;

	// 缓存Map
	private final static Map<String, Token> LOCAL_CACHE = new HashMap<String, TokenManager.Token>();

	// 复合体结构，含SSOUser与最后访问时间lassAccessTime两个成员
	private static class Token {
		private SSOUser user;
		private Date lastAccessTime;
	}

	private TokenManager() {
	}

	/**
	 * 验证vt的有效性
	 * 
	 * @param vt
	 * @return
	 * @throws IOException
	 */
	public static SSOUser validate(String vt) throws Exception {
		SSOUser user = localValidate(vt);

		if (user == null) {
			user = remoteValidate(vt);
		}
		if(user != null) {
			resetRedisExpires(vt);
		}

		return user;
	}

	/**
	 * 本地缓存验证vt有效性
	 * 
	 * @param vt
	 * @return
	 */
	private static SSOUser localValidate(String vt) {
		// 从缓存中查找数据
		Token token = LOCAL_CACHE.get(vt);

		if (token != null) { // 用户数据存在
			logger.debug("本地缓存验证vt有效性");
			// 更新最后返回时间
			token.lastAccessTime = new Date();
			// 返回结果
			return token.user;
		}

		return null;
	}

	/**
	 * 远程验证vt有效性
	 * 
	 * @param token
	 * @return
	 * @throws IOException
	 */
	private static SSOUser remoteValidate(String token) throws Exception {
		String validateUrl = serverInnerAddress + "/api/sso/validate_token?token=" + token;
		HttpResult result = HttpHelper.executeGet(validateUrl);

		SSOUser user = null;

		if (result.getStatusCode() == 404) {
			throw new Exception("远程验证token失败，请检查URL是否正确：" + validateUrl);
		}

		if (result.getStatusCode() == 200) {
			String content = result.getContent();
			user = JSON.parseObject(content, SSOUser.class) ;
			if(user != null) {
				logger.debug("远程验证vt有效性");
				// 处理本地缓存
				cacheUser(token, user);
			} else {
				logger.debug("远程验证vt不存在");
			}
			
		}
		return user;
	}

	/**
	 * 验证成功后将信息写入本地缓存
	 * 
	 * @param vt
	 * @param user
	 */
	private static void cacheUser(String vt, SSOUser user) {
		Token token = new Token();
		token.user = user;
		token.lastAccessTime = new Date();
		LOCAL_CACHE.put(vt, token);
	}

	/**
	 * 重置Cookie过期时间
	 * 
	 * @param vt
	 */
	public static void resetCookieTime(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, int cookieMaxage) {
		CookieUtils.setCookie(request, response, cookieName, cookieValue, cookieMaxage);
	}
	
	/**
	 * 验证Token是否过期
	 * @param vt
	 * @param cookieExpires
	 * @return
	 */
	public static Date timeout(String vt, long cookieExpires) {
		Token token = LOCAL_CACHE.get(vt) ;
		
		if(null != token) {
			Date lastAccessTime = token.lastAccessTime ;
			// 最终过期日期(最后访问时间+过期时间)
			Date expires = new Date(lastAccessTime.getTime() + (cookieExpires * 1000)) ;
			Date now = new Date() ;
			
			if(expires.compareTo(now) < 0) { //已过期
				//从本地缓存移除
				LOCAL_CACHE.remove(vt) ;
				//返回null表示此客户端缓存已过期
				return null ;
			} else {
				//重新设置缓存的过期时间
				token.lastAccessTime = expires ;
				
				//移除所有过期的缓存
				removeExpires();
				
				//返回延期时间
				return expires ;
			}
		} else {
			return null ;
		}
	}
	
	/**
	 * 移除所有过期的缓存
	 */
	public static void removeExpires() {
		Date now = new Date() ;
		//过期的Token
		List<String> expiresTokens = new ArrayList<String>();
		//获取所有过期的Token
		for (String key : LOCAL_CACHE.keySet()) {
			Token t = LOCAL_CACHE.get(key);
			if(t.lastAccessTime.compareTo(now) < 0) {
				expiresTokens.add(key);
				logger.debug("移除缓存中过期的Token：[{}],过期时间[{}],用户[{}]", key, DateUtil.formatDateTime(t.lastAccessTime), t.user.getRealName());
			} else {
				logger.debug("用户：[{}], 过期时间：[{}], Token：[{}]", t.user.getRealName(), DateUtil.formatDateTime(t.lastAccessTime), key);
			}
		}
		//移除所有过期的缓存
		for (String key : expiresTokens) {
			LOCAL_CACHE.remove(key) ;
		}
	}
	
	/**
	 * 重设Redis缓存用户过期时间
	 * @param token
	 */
	public static void resetRedisExpires(String token) {
		try {
			HttpHelper.executeGet(serverInnerAddress + "/api/sso/reset_expire/"+token);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 用户退出时失效对应缓存
	 * @param vt
	 */
	public static void invalidate(String vt) {
		//从本地缓存移除
		LOCAL_CACHE.remove(vt) ;
	}
	
	/**
	 * 服务器端应用关闭时情况本地缓存，失效所有信息
	 */
	public static void destory() {
		LOCAL_CACHE.clear();
	}

}
